/* generate the fibonacci sequence using coroutines
   Copyright (C) 2018 Ariadne Devos

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdio.h>

#include "../lco.h"

static _Noreturn void
produce_fibonacci(struct lco_coroutine *current);

static _Noreturn void
consume_fibonacci(struct lco_coroutine *current);

static struct lco_coroutine print = {};

static struct lco_coroutine fibonacci = {};

static struct lco_position thread;

static unsigned long c;

void
produce_fibonacci(struct lco_coroutine *current)
{
	unsigned long a = 0, b = 1;
	for (int i = 0; i < 80; i++) {
		c = a + b;
		a = b;
		b = c;
		lco_continue(&print, current, 0);
	}
	lco_pause(current, &thread, 0);
}

void
consume_fibonacci(struct lco_coroutine *current)
{
	while (1) {
		fprintf(stdout, "%ld\n", c);
		lco_continue(&fibonacci, current, 0);
	}
}

#define STACKSIZE 16384

_Alignas(LCO_STACK_ALIGNMENT)
static char stack0[STACKSIZE];
_Alignas(LCO_STACK_ALIGNMENT)
static char stack1[STACKSIZE];
_Alignas(LCO_STACK_ALIGNMENT)
static char stack2[STACKSIZE];

int
main(int argc, char **argv)
{
	/* XXX write a program to generate this*/
	print.entry = &consume_fibonacci;
	print.stack_size = STACKSIZE;
	print.stack_array = stack0;

	fibonacci.entry = &produce_fibonacci;
	fibonacci.stack_size = STACKSIZE;
	fibonacci.stack_array = stack1;

	lco_auto_sigaltstack(STACKSIZE, stack2);

	lco_init(&print);
	lco_init(&fibonacci);

	lco_resume(&fibonacci, &thread, 0);
	return 0;
}

